package cn.gov.mwr.sl651.command;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


import cn.gov.mwr.sl651.appendix.FD4;
import org.apache.commons.lang3.StringUtils;

import cn.gov.mwr.sl651.AbstractBody;
import cn.gov.mwr.sl651.Command;
import cn.gov.mwr.sl651.HydroBuilder;
import cn.gov.mwr.sl651.IMessage;
import cn.gov.mwr.sl651.Symbol;
import cn.gov.mwr.sl651.appendix.FC;
import cn.gov.mwr.sl651.body.DataItem;
import cn.gov.mwr.sl651.body.Down38Body;
import cn.gov.mwr.sl651.body.Down3ABody;
import cn.gov.mwr.sl651.body.Down40Body;
import cn.gov.mwr.sl651.body.Down49Body;
import cn.gov.mwr.sl651.body.Down4CBody;
import cn.gov.mwr.sl651.body.DownBaseBody;
import cn.gov.mwr.sl651.body.DownE2Body;
import cn.gov.mwr.sl651.parser.HexParser;
import cn.gov.mwr.sl651.utils.ByteUtil;

/**
 * 下发命令类，用于中心站向遥测站发送请求
 *
 * @author lipujun
 * @ClassName: DownCommand
 * @Description: 下发命令类
 * @date Mar 1, 2013
 */
public class DownCommand extends Command {

    /**
     * M2、M4模式如下：
     * <p>
     * 报文起始符为：STX
     * <p>
     * 报文结束符为：ENQ、ACK、EOT、ESC。 ENQ表示请求；ACK表示应答；EOT表示；ESC表示
     * <p>
     * ------------------------------------------------------------------------
     * <p>
     * M3模式如下：
     * <p>
     * 报文起始符为：SYN。表示多包发送，一次确认的传输模式中使用
     * <p>
     * 报文结束符为：ENQ、ACK、EOT、ESC。
     * ENQ表示请求；ACK表示作为有后续报文帧的“确认”结束符；EOT表示作为传输结束确认帧报文符，表示可以退出通信
     * 。；ESC表示传输结束，终端保持在线
     *
     * @param funcCode
     * @param eof
     */
    public DownCommand() {
        super( Symbol.DOWN );
    }

    /**
     * @param sequence
     * @param date
     * @return
     */
    public IMessage sendReplyMessage(int sequence, String date) {
        AbstractBody body = new DownBaseBody();
        initBase( body, sequence, date );
        return buildMessage( body );
    }

    /**
     * 中心站查询遥测站实时数据
     * <p>
     * 功能码：36
     */
    public IMessage send36Message(int sequence, String date) {
        AbstractBody body = new DownBaseBody();
        initBase( body, sequence, date );
        return buildMessage( body );
    }

    /**
     * 中心站查询遥测站实时数据
     * <p>
     * 功能码：37
     */
    public IMessage send37Message(int sequence, String date) {
        AbstractBody body = new DownBaseBody();
        initBase( body, sequence, date );
        return buildMessage( body );
    }

    /**
     * 中心站查询遥测站时段数据
     * <p>
     * 功能码：38
     */
    public IMessage send38Message(int sequence, String date, String startTime,
                                  String endTime, String drx, String itemList) {
        Down38Body body = new Down38Body();
        body.setSerialId( sequence );// 流水号 ，取值范围01 - 65535

        if (StringUtils.isBlank( date )) {
            SimpleDateFormat sdf = new SimpleDateFormat( "yyMMddHHmmss" );
            body.setSendDate( sdf.format( new Date() ) );
        } else {
            body.setSendDate( date );// 发报时间
        }

        body.setStartTime( startTime );
        body.setEndTime( endTime );

        body.setDrx( drx );// 1-31

        if (StringUtils.isNotBlank( itemList )) {
            String[] itemArray = itemList.split( "," );
            ArrayList<byte[]> items = new ArrayList<byte[]>();
            for (int i = 0; i < itemArray.length; i++) {
                String hexStr = itemArray[i];
                if (StringUtils.isNotBlank( hexStr.trim() )) {
                    byte[] item = ByteUtil.HexStringToBinary( itemArray[i] );
                    items.add( item );

                    FC fc = getFcHex( hexStr );
                    byte dataLen = HydroBuilder.buildDataLength( fc.getLen(), fc
                            .getDecimal() );
                    items.add( new byte[]{dataLen} );

                }
            }
            body.setItems( items );
        }

        return buildMessage( body );

    }

    /**
     * 中心站查询遥测站人工置数
     * <p>
     * 功能码：39
     * <p>
     * 注意：数据格式与37类似
     */
    public IMessage send39Message(int sequence, String date) {
        // AbstractBody body = new DownBase();
        // initBase(body,sequence, date);
        return send37Message( sequence, date );
    }

    /**
     * 中心站查询遥测站指定要素实时数据
     * <p>
     * 功能码：3A
     */
    public IMessage send3AMessage(int sequence, String date, String itemList) {
        Down3ABody body = new Down3ABody();
        initBase( body, sequence, date );
        if (StringUtils.isNotBlank( itemList )) {
            String[] itemArray = itemList.split( "," );
            ArrayList<byte[]> items = new ArrayList<byte[]>();
            for (int i = 0; i < itemArray.length; i++) {
                String hexStr = itemArray[i];
                if (StringUtils.isNotBlank( hexStr.trim() )) {
                    byte[] item = ByteUtil.HexStringToBinary( itemArray[i] );
                    items.add( item );

                    FC fc = getFcHex( hexStr );
                    byte dataLen = HydroBuilder.buildDataLength( fc.getLen(), fc
                            .getDecimal() );
                    items.add( new byte[]{dataLen} );


                }
            }
            body.setItems( items );
        }
        return buildMessage( body );

    }


    public FC getDataFc(String hexStr) {
        FC fc = getFcHex( hexStr );
        return fc;
        /*
        byte dataLen = HydroBuilder.buildDataLength( fc.getLen(), fc.getDecimal() );
        return dataLen;
        */
    }
    public FD4 getDataFd4(String hexStr) {
        FD4 fd4 = getFd4Hex( hexStr );
        return fd4;
        /*
        byte dataLen = HydroBuilder.buildDataLength( fc.getLen(), fc.getDecimal() );
        return dataLen;
        */
    }

    /**
     * 中心站修改遥测站基本配置表
     * <p>
     * 功能码：40
     */
    public IMessage send40Message(int sequence, String date,
                                  List<DataItem> items) {
        Down40Body body = new Down40Body();
        initBase( body, sequence, date );
        body.setItems( items );
        return buildMessage( body );
    }

    /**
     * 中心站读取遥测站基本配置表
     * <p>
     * 功能码：41
     * <p>
     * 注意：数据格式与3A类似
     */
    public IMessage send41Message(int sequence, String date, String itemList) {
        return send3AMessage( sequence, date, itemList );
    }

    /**
     * 中心站修改遥测站运行参数配置表
     * <p>
     * 功能码：42
     * <p>
     * 注意：功能与40类似
     */
    public IMessage send42Message(int sequence, String date,
                                  List<DataItem> items) {
        return send40Message( sequence, date, items );
        //return send3AMessage(sequence, date, itemList);
    }

    /**
     * 中心站读取遥测站运行参数配置表
     * <p>
     * 功能码：43
     */
    public IMessage send43Message(int sequence, String date, String itemList) {
        return send3AMessage( sequence, date, itemList );
    }

    /**
     * 中心站查询水泵电机实时工作数据
     * <p>
     * 功能码：44
     */
    public IMessage send44Message(int sequence, String date) {
        return send37Message( sequence, date );
    }

    /**
     * 中心站查询遥测站软件版本
     * <p>
     * 功能码：45
     */
    public IMessage send45Message(int sequence, String date) {
        return send37Message( sequence, date );
    }

    /**
     * 中心站查询遥测站状态和报警信息
     * <p>
     * 功能码：46
     */
    public IMessage send46Message(int sequence, String date) {
        return send37Message( sequence, date );
    }

    /**
     * 初始化固态存储数据，标识符为固定97H
     * <p>
     * 功能码：47
     */
    public IMessage send47Message(int sequence, String date) {
        return send3AMessage( sequence, date, "97" );
    }

    /**
     * 恢复遥测站出厂设置，标识符为固定98H
     * <p>
     * 功能码：48
     */
    public IMessage send48Message(int sequence, String date) {
        return send3AMessage( sequence, date, "98" );
    }

    /**
     * 修改密码，密码标识符为03H
     * <p>
     * 功能码：49
     */
    public IMessage send49Message(int sequence, String date, String oldPwd,
                                  String newPwd) {
        Down49Body body = new Down49Body();
        initBase( body, sequence, date );
        body.setOldPwd( oldPwd );
        body.setNewPwd( newPwd );
        return buildMessage( body );
    }

    /**
     * 设置遥测站时钟
     * <p>
     * 功能码：4A
     */
    public IMessage send4AMessage(int sequence, String date) {
        return send37Message( sequence, date );
    }

    /**
     * 设置遥测站IC卡状态
     * <p>
     * 功能码：4B
     */
    public IMessage send4BMessage(int sequence, String date, String data) {

        byte[] bytes = new byte[6];

        bytes[0] = (byte) 0x45;
        bytes[1] = HydroBuilder.buildDataLength( 4, 0 );

        bytes[2] = (byte) 0x00;// "00000000"
        bytes[3] = data.equals( "1" ) ? (byte) 0x40 : (byte) 0x00;// "0" + data +
        // "000000";
        bytes[4] = (byte) 0x00;// "00000000"
        bytes[5] = (byte) 0x00;// "00000000"

        return send4CMessage( sequence, date, bytes );
    }

    /**
     * 控制水泵开关命令/水泵状态自报
     * <p>
     * 功能码：4C
     */
    public IMessage send4CMessage(int sequence, String date, byte[] data) {
        Down4CBody body = new Down4CBody();
        initBase( body, sequence, date );
        body.setData( data );
        return buildMessage( body );
    }

    /**
     * 控制阀门开关命令/阀门状态信息自报
     * <p>
     * 功能码：4D
     * <p>
     * 注意：此操作与4C功能类似
     */
    public IMessage send4CMessage(int sequence, String date, String binstr) {
        int count = binstr.length();

        int len = count / 8;
        if (count % 8 != 0)
            len = len + 1;

        byte[] bytes = new byte[1 + len];
        bytes[0] = ByteUtil.ubyteToBytes( len )[0];

        binstr = StringUtils.leftPad( binstr, len * 8, "0" );

        for (int i = 0; i < len; i++) {
            String substr = binstr.substring( i * 8, (i + 1) * 8 );

            Integer temp = Integer.valueOf( substr, 2 );
            bytes[i + 1] = ByteUtil.ubyteToBytes( temp )[0];

        }

        return send4CMessage( sequence, date, bytes );
    }

    /**
     * 控制阀门开关命令/阀门状态信息自报
     * <p>
     * 功能码：4D
     * <p>
     * 注意：此操作与4C功能类似
     */
    public IMessage send4DMessage(int sequence, String date, String binstr) {
        return send4CMessage( sequence, date, binstr );
    }

    /**
     * 控制闸门开关命令/闸门状态信息自报
     * <p>
     * 功能码：4E
     */
    public IMessage send4EMessage(int sequence, String date, int count,
                                  String binaryString, String kdstr) {

        int len = count / 8;
        if (count % 8 != 0)
            len = len + 1;

        byte[] bytes = new byte[1 + len + count * 2];
        bytes[0] = ByteUtil.ubyteToBytes( len )[0];

        binaryString = StringUtils.rightPad( binaryString, len * 8, "0" );
        for (int i = 0; i < len; i++) {
            String substr = binaryString.substring( i * 8, (i + 1) * 8 );
            // byte temp = Byte.valueOf(substr, 2);
            // Integer temp = Integer.valueOf(binaryString, 2); //
            // binaryString为二进制字符串

            Integer temp = Integer.valueOf( substr, 2 ); //
            bytes[i + 1] = ByteUtil.ubyteToBytes( temp )[0];
            System.out.println( ByteUtil
                    .toHexString( ByteUtil.ubyteToBytes( temp ) ) );
        }

        // byte[] height = ByteUtil.str2Bcd(StringUtils.leftPad(kdstr, 4, "0"));
        kdstr = StringUtils.rightPad( kdstr, count * 4, "0" );
        for (int i = 0; i < count; i++) {
            String tempstr = kdstr.substring( i * 4, i * 4 + 4 );
            byte[] height = ByteUtil.str2Bcd( tempstr );
            bytes[1 + len + i * 2] = height[0];
            bytes[1 + len + i * 2 + 1] = height[1];
        }

        return send4CMessage( sequence, date, bytes );

    }

    /**
     * 水量定值控制命令
     * <p>
     * 功能码：4F
     * <p>
     * status为1字节HEX码，“FFH”表示定值控制投入，“00H”表示定值控制退出。确认报文不编列该项。
     */
    public IMessage send4FMessage(int sequence, String date, String status) {
        return send3AMessage( sequence, date, status );

    }

    /**
     * 中心站查询遥测站事件记录
     * <p>
     * 功能码：50
     */
    public IMessage send50Message(int sequence, String date) {
        return send37Message( sequence, date );
    }

    /**
     * 中心站查询遥测站时钟
     * <p>
     * 功能码：51
     */
    public IMessage send51Message(int sequence, String date) {
        return send37Message( sequence, date );
    }

    /**
     * 构造重发报文
     * <p>
     * 功能码：51
     */
    public IMessage sendRepeatMessage(int sequence, String date) {
        return send37Message( sequence, date );
    }

    /**
     * 远程升级功能，标识符为E2H
     * <p>
     * 功能码：E2
     */
    public IMessage sendE2Message(int sequence, String date, String ver) {
        Down4CBody body = new Down4CBody();
        initBase( body, sequence, date );
        body.setData( ver.getBytes() );
        return buildMessage( body );
    }

    public IMessage sendE2Message(byte[] content) {
        DownE2Body body = new DownE2Body();

        body.setData( content );
        return buildMessage( body );
    }


    public static void main(String[] args) {
        DownCommand cmd = new DownCommand();
        cmd.setStartBit( Symbol.SOH_ASC );

        if (cmd.getStartBit() == Symbol.SOH_ASC) {
            cmd.setCenterAddr( "FF".getBytes() );
            cmd.setStationAddr( "0011223344".getBytes() );
            cmd.setPassword( "FFFF".getBytes() );
        } else {
            cmd.setCenterAddr( ByteUtil.HexStringToBinary( "FF" ) );
            cmd.setStationAddr( ByteUtil.HexStringToBinary( "0011223344" ) );
            cmd.setPassword( ByteUtil.HexStringToBinary( "FFFF" ) );
        }

//		cmd.sendRepeatMessage(1, null);
//
        IMessage msg = cmd.sendReplyMessage( 1, null );
        System.out.print( ">> hex " + cmd.printHexString( msg ) );
//		cmd.send4EMessage(1, null, 22, "11110", "1234");

        // cmd.setBodyStartBit(Symbol.STX);
        // cmd.setEof(Symbol.ENQ);
        //
        // IMessage message;
        // // ------------------------------
        // cmd.setFuncCode((byte) 0x37);
        // message = cmd.send37Message(1, null);
        // cmd.printHexString(message);
        // // ------------------------------
        //
        // cmd.setFuncCode((byte) 0x38);
        // message = cmd.send38Message(1, null, "13010108", "13030108",
        // "000600",
        // "F0,1F,20");
        // cmd.printHexString(message);
        //
        // // ------------------------------
        //
        // cmd.setFuncCode((byte) 0x3A);
        // message = cmd.send3AMessage(0x3A, null, "F0,1F,20");
        // cmd.printHexString(message);

    }
}
